﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using Defero.Core;
using Defero.Core.Entities;
using Defero.Infrastucture.Repositories;

namespace Defero.Infrastucture
{
    public class UnitOfWork<TEntity, TContext> : IUnitOfWork<TEntity> 
            where TEntity : EntityObject
            where TContext : ObjectContext, new()
    {
        /// <summary>
        /// Constructor default.
        /// </summary>
        /// <param name="context">Contexto de datos.</param>
        public UnitOfWork(Repository<TContext> parent)
        {
            Parent = parent;

            EntitySetName =
                Parent.Context.MetadataWorkspace.GetItems<EntityContainer>(DataSpace.CSpace)
                .First()
                .BaseEntitySets
                .FirstOrDefault(item => item.ElementType.Name == typeof(TEntity).Name)
                .Name;

        }
        /// <summary>
        /// Modifica el estado interno de una entidad
        /// </summary>
        /// <param name="entity">Entidad que sera modificada</param>
        /// <param name="state">Estado en el que se establecera la entidad.</param>
        TEntity SetState(TEntity entity, EntityState state) 
        {
            entity.EntityKey = Parent.Context.CreateEntityKey(EntitySetName, entity);

            Parent.Context.Attach(entity);
            Parent.Context.ObjectStateManager.ChangeObjectState(entity, state);
            
            return entity;
        }
        /// <summary>
        /// Repositorio al que pertenece
        /// </summary>
        Repository<TContext> Parent { get; set; }
        /// <summary>
        /// Nombre de la colleccion de entidades.
        /// </summary>
        string EntitySetName;
        /// <summary>
        /// Retorna todos los objetos disponibles.
        /// </summary>
        public IEnumerable<TEntity> All() 
        {
            return Parent.Context.CreateObjectSet<TEntity>().AsEnumerable();
        }
        /// <summary>
        /// Inserta un registro.
        /// </summary>
        /// <param name="entity">Objeto a insertar</param>
        public void Insert(TEntity entity)
        {
            SetState(entity, EntityState.Added);
        }
        /// <summary>
        /// Elimina un registro.
        /// </summary>
        /// <param name="entity">Objeto a eliminar.</param>
        public void Delete(TEntity entity)
        {
            SetState(entity, EntityState.Deleted);
        }
        /// <summary>
        /// Actualiza un registro.
        /// </summary>
        /// <param name="entity">Objeto a actualizar.</param>
        public void Save(TEntity entity)
        {
            SetState(entity, EntityState.Modified);
        }
        /// <summary>
        /// Ejecuta todos los cambios pendientes.
        /// </summary>
        public void Submit()
        {
            Submit(SaveOptions.AcceptAllChangesAfterSave);
        }
        /// <summary>
        /// Ejecuta todos los cambios pendientes.
        /// </summary>
        /// <param name="conflicMode">Modo de resolución de conflictos.</param>
        public void Submit(SaveOptions options)
        {
            Parent.Context.SaveChanges(options);
        }
    }
}
